
# 从 src.UI.Canvas.UICommon 模块导入 clearLayout 函数，用于清空布局
from src.UI.Canvas.UICommon import clearLayout
# 从 src.UI.Widgets.EditPropertiesWidget 模块导入 EditPropertiesTreeWidget 类
from src.UI.Widgets.EditPropertiesWidget import EditPropertiesTreeWidget
# 从 qtpy 库导入 QtWidgets 模块，提供各种 GUI 控件
from qtpy import QtWidgets
# 从 qtpy 库导入 QtCore 和 QtGui 模块，提供核心功能和图形处理功能
from qtpy import QtCore, QtGui


# Framework
class HeadButton(QtWidgets.QPushButton):
    """
    继承自 QtWidgets.QPushButton 的自定义按钮类，用于作为折叠控件的头部按钮。

    :param parent: 父控件，默认为 None
    :param maxHeight: 按钮的最大高度，默认为 25
    """

    def __init__(self, parent=None, maxHeight=25):
        # 调用父类的构造函数
        super(HeadButton, self).__init__(parent)
        # 设置对象名称为类名
        self.setObjectName(self.__class__.__name__)
        # 设置为默认按钮
        self.setDefault(True)
        # 设置按钮的最大高度
        self.setMaximumHeight(maxHeight)


class CollapsibleWidget(QtWidgets.QWidget):
    """
    可折叠的控件，包含一个头部按钮和一个内容控件，点击头部按钮可隐藏或显示内容。

    :param parent: 父控件，默认为 None
    :param headName: 头部按钮的名称，默认为 "Collapse"
    :param noSpacer: 是否移除间隔项，默认为 True
    :param collapsed: 初始是否折叠，默认为 False
    """

    def __init__(
        self, parent=None, headName="Collapse", noSpacer=True, collapsed=False
    ):
        # 调用父类的构造函数
        super(CollapsibleWidget, self).__init__(parent)
        # 设置对象名称为类名
        self.setObjectName(self.__class__.__name__)
        # 初始化 UI
        self.setupUi()
        # 连接 UI 信号和槽
        self.connectUi()
        # 设置头部按钮的名称
        self.setButtonName(headName)
        # 如果需要移除间隔项，则调用移除方法
        if noSpacer:
            self.removeSpacer()
        # 设置折叠状态
        self.setCollapsed(collapsed)

    def filterContent(self, pattern):
        """
        根据指定模式过滤内容，当前为空实现。

        :param pattern: 过滤模式
        """
        pass

    def title(self):
        """
        获取头部按钮的文本，作为控件的标题。

        :return: 头部按钮的文本
        """
        return self.pbHead.text()

    def setReadOnly(self, bReadOnly=True):
        """
        设置内容控件的只读状态。

        :param bReadOnly: 是否只读，默认为 True
        """
        self.ContentWidget.setEnabled(not bReadOnly)

    def connectUi(self):
        """
        连接头部按钮的点击信号到切换折叠状态的槽函数。
        """
        self.pbHead.clicked.connect(self.toggleCollapsed)

    def setupUi(self):
        """
        初始化 UI 界面，包括布局、控件的创建和设置。
        """
        # 设置控件的初始大小
        self.resize(400, 300)
        # 创建垂直布局并设置为控件的布局
        self.mainVLayout = QtWidgets.QVBoxLayout(self)
        # 设置布局的间距
        self.mainVLayout.setSpacing(2)
        # 设置布局的边距
        self.mainVLayout.setContentsMargins(2, 2, 2, 2)
        # 设置布局的对象名称
        self.mainVLayout.setObjectName("mainVLayout")
        # TODO: 修复未解析的特性引用问题
        self.mainVLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize)
        # 创建头部按钮
        self.pbHead = HeadButton(self)
        # 将头部按钮添加到布局中
        self.mainVLayout.addWidget(self.pbHead)
        # 设置控件的最小高度
        self.setMinimumHeight(30)
        # 创建内容控件
        self.ContentWidget = QtWidgets.QWidget(self)
        # TODO: 修复未解析的特性引用问题
        sizePolicy = QtWidgets.QSizePolicy(
            QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred
        )
        # 设置水平拉伸因子
        sizePolicy.setHorizontalStretch(0)
        # 设置垂直拉伸因子
        sizePolicy.setVerticalStretch(0)
        # 设置高度是否依赖宽度
        sizePolicy.setHeightForWidth(
            self.ContentWidget.sizePolicy().hasHeightForWidth()
        )
        # 为内容控件设置大小策略
        self.ContentWidget.setSizePolicy(sizePolicy)
        # 为当前控件设置大小策略
        self.setSizePolicy(
            QtWidgets.QSizePolicy(
                QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred
            )
        )
        # 设置内容控件的对象名称
        self.ContentWidget.setObjectName("ContentWidget")
        # 设置内容控件的边距
        self.ContentWidget.setContentsMargins(10, 0, 0, 0)
        # 将内容控件添加到布局中
        self.mainVLayout.addWidget(self.ContentWidget)
        # TODO: 修复未解析的特性引用问题
        self.spacerItem = QtWidgets.QSpacerItem(
            20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding
        )
        # 将间隔项添加到布局中
        self.mainVLayout.addItem(self.spacerItem)
        # 设置控件的窗口标题为对象名称
        self.setWindowTitle(self.objectName())
        # 为头部按钮添加样式表
        self.pbHead.setStyleSheet(self.pbHead.styleSheet() + "\nText-align:left;")
        # TODO: 修复未解析的特性引用问题
        self.contentHiddenIcon = self.pbHead.style().standardIcon(
            QtWidgets.QStyle.SP_TitleBarUnshadeButton
        )
        # TODO: 修复未解析的特性引用问题
        self.contentVisibleIcon = self.pbHead.style().standardIcon(
            QtWidgets.QStyle.SP_TitleBarShadeButton
        )
        # 更新图标
        self.updateIcon()

    def addWidget(self, widget):
        """
        向主布局中添加一个控件。

        :param widget: 要添加的控件
        """
        self.mainVLayout.addWidget(widget)

    def removeSpacer(self):
        """
        移除布局中的间隔项。
        """
        if self.spacerItem is not None:
            # 从布局中移除间隔项
            self.mainVLayout.removeItem(self.spacerItem)
            # 删除间隔项对象
            del self.spacerItem
            # 将间隔项引用置为 None
            self.spacerItem = None

    def setContentHiddenIcon(self, icon):
        """
        设置内容隐藏时的图标。

        :param icon: 要设置的图标
        """
        self.contentHiddenIcon = icon

    def setContentVisibleIcon(self, icon):
        """
        设置内容显示时的图标。

        :param icon: 要设置的图标
        """
        self.contentVisibleIcon = icon

    def toggleCollapsed(self):
        """
        切换内容控件的折叠状态。
        """
        if self.ContentWidget.isVisible():
            # 如果内容控件可见，则设置为折叠状态
            self.setCollapsed(True)
        else:
            # 如果内容控件不可见，则设置为展开状态
            self.setCollapsed(False)

    def setButtonName(self, name):
        """
        设置头部按钮的文本。

        :param name: 要设置的文本
        """
        self.pbHead.setText(name)

    def isCollapsed(self):
        """
        判断内容控件是否处于折叠状态。

        :return: 如果内容控件隐藏则返回 True，否则返回 False
        """
        return self.ContentWidget.isHidden()

    def updateIcon(self):
        """
        根据内容控件的折叠状态更新头部按钮的图标。
        """
        if self.isCollapsed():
            # 如果内容控件折叠，则设置隐藏图标
            self.pbHead.setIcon(self.contentHiddenIcon)
        else:
            # 如果内容控件展开，则设置显示图标
            self.pbHead.setIcon(self.contentVisibleIcon)

    def setCollapsed(self, bCollapsed=False):
        """
        设置内容控件的折叠状态，并更新图标。

        :param bCollapsed: 是否折叠，默认为 False
        """
        self.ContentWidget.setVisible(not bCollapsed)
        self.updateIcon()


class PropertyEntry(QtWidgets.QWidget):
    """
    用于显示属性标签和对应控件的组合控件。

    :param label: 属性标签文本
    :param widget: 对应的控件
    :param parent: 父控件，默认为 None
    :param hideLabel: 是否隐藏标签，默认为 False
    :param maxLabelWidth: 标签的最大宽度，默认为 None
    :param toolTip: 工具提示文本，默认为 ""
    """

    def __init__(
        self,
        label,
        widget,
        parent=None,
        hideLabel=False,
        maxLabelWidth=None,
        toolTip="",
    ):
        # 调用父类的构造函数
        super(PropertyEntry, self).__init__(parent)
        # 保存属性标签文本
        self.label = label
        # 创建水平布局并设置为控件的布局
        self.layout = QtWidgets.QHBoxLayout(self)
        # 设置布局的边距
        self.layout.setContentsMargins(1, 1, 1, 1)
        if not hideLabel:
            # 如果不隐藏标签，则创建标签控件
            label = QtWidgets.QLabel(label + ":")
            # 设置标签的样式为粗体
            label.setStyleSheet("font: bold")
            # 设置标签的工具提示
            label.setToolTip(toolTip)
            if not maxLabelWidth:
                # 如果未指定最大宽度，则设置标签的大小策略
                # TODO: 修复未解析的特性引用问题
                label.setSizePolicy(
                    QtWidgets.QSizePolicy(
                        QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred
                    )
                )
            else:
                # 如果指定了最大宽度，则设置标签的最大宽度
                label.setMaximumWidth(maxLabelWidth)
            # 将标签添加到布局中
            self.layout.addWidget(label)
        # 将对应的控件添加到布局中
        self.layout.addWidget(widget)
        # 初始化索引为 -1
        self.index = -1

    def getLabel(self):
        """
        获取属性标签文本。

        :return: 属性标签文本
        """
        return self.label


class CollapsibleFormWidget(CollapsibleWidget):
    """
    继承自 CollapsibleWidget 的可折叠表单控件，用于管理属性条目和分组。

    :param parent: 父控件，默认为 None
    :param headName: 头部按钮的名称，默认为 "Collapse"
    :param noSpacer: 是否移除间隔项，默认为 True
    :param collapsed: 初始是否折叠，默认为 False
    :param hideLabels: 是否隐藏属性标签，默认为 False
    """

    def __init__(
        self,
        parent=None,
        headName="Collapse",
        noSpacer=True,
        collapsed=False,
        hideLabels=False,
    ):
        # 调用父类的构造函数
        super(CollapsibleFormWidget, self).__init__(
            parent, headName=headName, noSpacer=noSpacer, collapsed=collapsed
        )
        # 保存是否隐藏标签的标志
        self.hideLabels = hideLabels
        # 在内容控件中创建垂直布局
        self.Layout = QtWidgets.QVBoxLayout(self.ContentWidget)
        # 设置布局的对象名称
        self.Layout.setObjectName("CollapseWidgetFormLayout")
        # 设置布局的间距
        self.Layout.setSpacing(2)
        # 设置布局的边距
        self.Layout.setContentsMargins(0, 0, 0, 5)
        # 初始化属性名称字典
        self.propertyNames = {}
        # 初始化条目名称字典
        self.entryNames = {}
        # 更新图标
        self.updateIcon()
        # 初始化分组字典
        self.groups = {}

    def setSpacing(self, spacing=2):
        """
        设置表单布局的间距。

        :param spacing: 间距值，默认为 2
        """
        self.Layout.setSpacing(spacing)

    def isAllWidgetsHidden(self):
        """
        判断表单布局中的所有控件是否都隐藏。

        :return: 如果所有控件都隐藏则返回 True，否则返回 False
        """
        # 获取布局中的控件数量
        count = self.Layout.count()
        # 初始化隐藏控件数量为 0
        hidden = 0
        for i in range(count):
            # 获取布局中的控件
            widget = self.Layout.itemAt(i).widget()
            if widget.isHidden():
                # 如果控件隐藏，则隐藏控件数量加 1
                hidden += 1
        return count == hidden

    def filterContent(self, pattern):
        """
        根据指定模式过滤表单内容，显示或隐藏属性条目和分组。

        :param pattern: 过滤模式
        """
        for key, value in self.entryNames.items():
            if isinstance(value, PropertyEntry):
                # 如果条目是 PropertyEntry 类型，则根据模式设置可见性
                value.setVisible(pattern.lower() in value.getLabel().lower())
        for key, value in self.groups.items():
            if isinstance(value, CollapsibleGroupBox):
                if value.isAllWidgetsHidden():
                    # 如果分组中的所有控件都隐藏，则隐藏分组
                    value.hide()
                else:
                    # 否则显示分组并展开
                    value.show()
                    value.setCollapsed(False)

    def insertWidget(
        self, index=0, label=None, widget=None, maxLabelWidth=None, group=None
    ):
        """
        在指定位置插入一个属性条目，可以选择添加到分组中。

        :param index: 插入位置，默认为 0
        :param label: 属性标签文本，默认为 None
        :param widget: 对应的控件，默认为 None
        :param maxLabelWidth: 标签的最大宽度，默认为 None
        :param group: 分组名称，默认为 None
        :return: 如果插入成功则返回 True，否则返回 False
        """
        if widget is None or isinstance(widget, CollapsibleWidget):
            return False
        if group is not None and group != "":
            if group in self.groups:
                # 如果分组已存在，则获取分组控件
                groupW = self.groups[group]
            else:
                # 如果分组不存在，则创建分组控件并添加到分组字典中
                groupW = CollapsibleGroupBox(group)
                self.groups[group] = groupW
        # 创建属性条目
        entry = PropertyEntry(
            str(label), widget, hideLabel=self.hideLabels, maxLabelWidth=maxLabelWidth
        )
        # 将属性名称和对应控件保存到字典中
        self.propertyNames[label] = widget
        # 将属性名称和对应条目保存到字典中
        self.entryNames[label] = entry
        if group is None or group == "":
            # 如果不指定分组，则直接插入到表单布局中
            self.Layout.insertWidget(index, entry)
        else:
            # TODO: 修复局部变量可能在赋值前引用的问题
            groupW.insertWidget(index, entry)
            # 将分组控件添加到表单布局中
            self.Layout.addWidget(groupW)
        return True

    def addWidget(self, label=None, widget=None, maxLabelWidth=None, group=None):
        """
        在表单布局中添加一个属性条目，可以选择添加到分组中。

        :param label: 属性标签文本，默认为 None
        :param widget: 对应的控件，默认为 None
        :param maxLabelWidth: 标签的最大宽度，默认为 None
        :param group: 分组名称，默认为 None
        :return: 如果添加成功则返回 True，否则返回 False
        """
        if widget is None or isinstance(widget, CollapsibleWidget):
            return False
        if group is not None and group != "":
            if group in self.groups:
                # 如果分组已存在，则获取分组控件
                groupW = self.groups[group]
            else:
                # 如果分组不存在，则创建分组控件并添加到分组字典中
                groupW = CollapsibleGroupBox(group)
                self.groups[group] = groupW
        # 将属性名称和对应控件保存到字典中
        self.propertyNames[label] = widget
        # TODO: 修复在 'None' 中找不到引用 'toolTip' 的问题
        entry = PropertyEntry(
            str(label),
            widget,
            hideLabel=self.hideLabels,
            maxLabelWidth=maxLabelWidth,
            toolTip=widget.toolTip(),
        )
        # 将属性名称和对应条目保存到字典中
        self.entryNames[label] = entry
        if group is None or group == "":
            # 如果不指定分组，则直接添加到表单布局中
            self.Layout.addWidget(entry)
        else:
            # TODO: 修复局部变量可能在赋值前引用的问题
            groupW.addWidget(entry)
            # 将分组控件添加到表单布局中
            self.Layout.addWidget(groupW)
        return True

    def getWidgetByName(self, name):
        """
        根据属性名称获取对应的控件。

        :param name: 属性名称
        :return: 如果找到则返回对应的控件，否则返回 None
        """
        if name in self.propertyNames:
            return self.propertyNames[name]
        else:
            return None


class CollapsibleGroupBox(QtWidgets.QWidget):
    """
    可折叠的分组框，包含一个带复选框的分组框和一个垂直布局。

    :param name: 分组框的名称
    """

    def __init__(self, name):
        # 调用父类的构造函数
        super(CollapsibleGroupBox, self).__init__()

        # widgets
        # 创建带复选框的分组框
        self.controlGroup = QtWidgets.QGroupBox()
        # 设置分组框的标题
        self.controlGroup.setTitle(name)
        # 设置分组框可复选
        self.controlGroup.setCheckable(True)
        # 设置分组框初始为选中状态
        self.controlGroup.setChecked(True)

        # groupbox layout
        # 在分组框中创建垂直布局
        self.groupLayout = QtWidgets.QVBoxLayout(self.controlGroup)

        # 设置分组框的固定高度为合适高度
        self.controlGroup.setFixedHeight(self.controlGroup.sizeHint().height())

        # signals
        # 连接分组框的复选框状态改变信号到切换折叠状态的槽函数
        self.controlGroup.toggled.connect(lambda: self.toggleCollapsed())

        # layout
        # 创建网格布局并设置为控件的布局
        self.mainLayout = QtWidgets.QGridLayout(self)
        # 将分组框添加到网格布局中
        self.mainLayout.addWidget(self.controlGroup)

    def isAllWidgetsHidden(self):
        """
        判断分组框布局中的所有控件是否都隐藏。

        :return: 如果所有控件都隐藏则返回 True，否则返回 False
        """
        # 获取布局中的控件数量
        count = self.groupLayout.count()
        # 初始化隐藏控件数量为 0
        hidden = 0
        for i in range(count):
            # 获取布局中的控件
            widget = self.groupLayout.itemAt(i).widget()
            if widget.isHidden():
                # 如果控件隐藏，则隐藏控件数量加 1
                hidden += 1
        return count == hidden

    def insertWidget(self, index, widget):
        """
        在指定位置插入一个控件到分组框布局中，并更新分组框的高度。

        :param index: 插入位置
        :param widget: 要插入的控件
        """
        self.groupLayout.insertWidget(index, widget)
        # 设置分组框的固定高度为合适高度
        self.controlGroup.setFixedHeight(self.controlGroup.sizeHint().height())

    def addWidget(self, widget):
        """
        在分组框布局中添加一个控件，并更新分组框的高度。

        :param widget: 要添加的控件
        """
        self.groupLayout.addWidget(widget)
        # 设置分组框的固定高度为合适高度
        self.controlGroup.setFixedHeight(self.controlGroup.sizeHint().height())

    def toggleCollapsed(self):
        """
        根据分组框的复选框状态切换折叠状态，更新分组框的高度。
        """
        # 获取分组框的复选框状态
        state = self.controlGroup.isChecked()
        if state:
            # 如果选中，则设置分组框的固定高度为合适高度
            self.controlGroup.setFixedHeight(self.controlGroup.sizeHint().height())
        else:
            # 如果未选中，则设置分组框的固定高度为 30
            self.controlGroup.setFixedHeight(30)

    def setCollapsed(self, bCollapsed=False):
        """
        设置分组框的折叠状态，并更新分组框的高度。

        :param bCollapsed: 是否折叠，默认为 False
        """
        self.controlGroup.setChecked(not bCollapsed)
        if not bCollapsed:
            # 如果不折叠，则设置分组框的固定高度为合适高度
            self.controlGroup.setFixedHeight(self.controlGroup.sizeHint().height())
        else:
            # 如果折叠，则设置分组框的固定高度为 30
            self.controlGroup.setFixedHeight(30)


class PropertiesWidget(QtWidgets.QWidget):
    """
    用于显示和管理属性的控件，包含搜索框、锁定按钮、复制按钮等。

    :param parent: 父控件，默认为 None
    :param searchByHeaders: 是否仅按标题搜索，默认为 False
    """
    # 定义一个信号，用于触发创建副本
    spawnDuplicate = QtCore.Signal()

    def __init__(self, parent=None, searchByHeaders=False):
        # 调用父类的构造函数
        super(PropertiesWidget, self).__init__(parent)
        # 设置控件的窗口标题
        self.setWindowTitle("Properties view")
        # 创建垂直布局并设置为控件的布局
        self.mainLayout = QtWidgets.QVBoxLayout(self)
        # 设置布局的对象名称
        self.mainLayout.setObjectName("propertiesMainLayout")
        # 设置布局的边距
        self.mainLayout.setContentsMargins(2, 2, 2, 2)
        # 创建搜索框
        self.searchBox = QtWidgets.QLineEdit(self)
        # 设置搜索框的对象名称
        self.searchBox.setObjectName("lineEdit")
        # 设置搜索框的占位文本
        self.searchBox.setPlaceholderText("search...")
        # 连接搜索框的文本变化信号到相应的过滤函数
        self.searchBox.textChanged.connect(
            self.filterByHeaders if searchByHeaders else self.filterByHeadersAndFields
        )
        # 创建搜索框的容器控件
        self.searchBoxWidget = QtWidgets.QWidget()
        # 创建搜索框容器的水平布局
        self.searchBoxLayout = QtWidgets.QHBoxLayout(self.searchBoxWidget)
        # 设置布局的边距
        self.searchBoxLayout.setContentsMargins(1, 1, 1, 1)
        # 将搜索框添加到容器布局中
        self.searchBoxLayout.addWidget(self.searchBox)

        # self.settingsButton = QtWidgets.QToolButton()
        # self.settingsButton.setIcon(QtGui.QIcon(":/settings.png"))
        # self.settingsMenu = QtWidgets.QMenu()
        # self.editPropertiesAction = QtWidgets.QAction("Edit Parameter Interface", None)
        # self.settingsMenu.addAction(self.editPropertiesAction)
        # self.settingsButton.setMenu(self.settingsMenu)
        # self.editPropertiesAction.triggered.connect(self.showPropertyEditor)
        # self.settingsButton.clicked.connect(self.spawnDuplicate.emit)
        # self.settingsButton.setPopupMode(QtWidgets.QToolButton.InstantPopup)

        # 创建锁定复选框
        self.lockCheckBox = QtWidgets.QToolButton()
        # 设置锁定复选框可复选
        self.lockCheckBox.setCheckable(True)
        # 设置锁定复选框的图标
        self.lockCheckBox.setIcon(QtGui.QIcon(":/unlocked.png"))
        # 连接锁定复选框的状态变化信号到切换图标函数
        self.lockCheckBox.toggled.connect(self.changeLockIcon)
        # 将锁定复选框添加到搜索框容器布局中
        self.searchBoxLayout.addWidget(self.lockCheckBox)
        # 创建复制按钮
        self.tearOffCopy = QtWidgets.QToolButton()
        # 设置复制按钮的图标
        self.tearOffCopy.setIcon(QtGui.QIcon(":/tear_off_copy_bw.png"))
        # 连接复制按钮的点击信号到触发创建副本信号
        self.tearOffCopy.clicked.connect(self.spawnDuplicate.emit)
        # 将复制按钮添加到搜索框容器布局中
        self.searchBoxLayout.addWidget(self.tearOffCopy)
        # 将搜索框容器控件添加到主布局中
        self.mainLayout.addWidget(self.searchBoxWidget)
        # 隐藏搜索框容器控件
        self.searchBoxWidget.hide()
        # 创建内容布局
        self.contentLayout = QtWidgets.QVBoxLayout()
        # TODO: 修复未解析的特性引用问题
        self.contentLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize)
        # 将内容布局添加到主布局中
        self.mainLayout.addLayout(self.contentLayout)
        # TODO: 修复未解析的特性引用问题
        self.spacerItem = QtWidgets.QSpacerItem(
            20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding
        )
        # 将间隔项添加到主布局中
        self.mainLayout.addItem(self.spacerItem)
        # TODO: 修复未解析的特性引用问题
        self.mainLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize)
        # TODO: 修复未解析的特性引用问题
        self.setSizePolicy(
            QtWidgets.QSizePolicy(
                QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
            )
        )

    def changeLockIcon(self, checked):
        """
        根据锁定复选框的状态切换图标。

        :param checked: 锁定复选框是否选中
        """
        if checked:
            # 如果选中，则设置锁定图标
            self.lockCheckBox.setIcon(QtGui.QIcon(":/locked.png"))
        else:
            # 如果未选中，则设置解锁图标
            self.lockCheckBox.setIcon(QtGui.QIcon(":/unlocked.png"))

    def setLockCheckBoxVisible(self, bVisible):
        """
        设置锁定复选框的可见性。

        :param bVisible: 是否可见
        """
        self.lockCheckBox.setVisible(bVisible)

    def setTearOffCopyVisible(self, bVisible):
        """
        设置复制按钮的可见性。

        :param bVisible: 是否可见
        """
        self.tearOffCopy.setVisible(bVisible)

    def setSearchBoxVisible(self, bVisible):
        """
        设置搜索框的可见性。

        :param bVisible: 是否可见
        """
        self.searchBox.setVisible(bVisible)

    def filterByHeaders(self, text):
        """
        根据标题过滤内容布局中的控件，显示或隐藏控件。

        :param text: 过滤文本
        """
        # 获取内容布局中的控件数量
        count = self.contentLayout.count()
        for i in range(count):
            # 获取布局中的项
            item = self.contentLayout.itemAt(i)
            # 获取项对应的控件
            w = item.widget()
            if w:
                # TODO: 修复未解析的特性引用问题
                if text.lower() in w.title().lower():
                    # 如果标题包含过滤文本，则显示控件
                    w.show()
                else:
                    # 否则隐藏控件
                    w.hide()

    def filterByHeadersAndFields(self, text):
        """
        根据标题和字段过滤内容布局中的控件，显示或隐藏控件，并展开符合条件的控件。

        :param text: 过滤文本
        """
        # 获取内容布局中的控件数量
        count = self.contentLayout.count()
        for i in range(count):
            # 获取布局中的项
            item = self.contentLayout.itemAt(i)
            # 获取项对应的控件
            w = item.widget()
            if w:
                # TODO: 修复未解析的特性引用问题
                w.filterContent(text)
                # TODO: 修复未解析的特性引用问题
                if w.isAllWidgetsHidden():
                    # 如果控件中的所有子控件都隐藏，则隐藏控件
                    w.hide()
                else:
                    # 否则显示控件并展开
                    w.show()
                    # TODO: 修复未解析的特性引用问题
                    w.setCollapsed(False)

    def isLocked(self):
        """
        判断锁定复选框是否选中。

        :return: 如果选中则返回 True，否则返回 False
        """
        return self.lockCheckBox.isChecked() == True

    def clear(self):
        """
        如果未锁定，则清空内容布局，隐藏搜索框容器控件，并将锁定复选框设置为未选中状态。
        """
        if not self.isLocked():
            # 清空内容布局
            clearLayout(self.contentLayout)
            # 隐藏搜索框容器控件
            self.searchBoxWidget.hide()
            # 将锁定复选框设置为未选中状态
            self.lockCheckBox.setChecked(False)

    def insertWidget(self, collapsibleWidget, index):
        """
        如果未锁定且传入的控件是 CollapsibleFormWidget 类型，则在指定位置插入控件到内容布局中，并显示搜索框容器控件。

        :param collapsibleWidget: 要插入的控件
        :param index: 插入位置
        :return: 如果插入成功则返回 True，否则返回 False
        """
        if not self.isLocked():
            if isinstance(collapsibleWidget, CollapsibleFormWidget):
                # 显示搜索框容器控件
                self.searchBoxWidget.show()
                # 在指定位置插入控件到内容布局中
                self.contentLayout.insertWidget(index, collapsibleWidget)
                return True

    def addWidget(self, collapsibleWidget):
        """
        如果未锁定且传入的控件是 CollapsibleFormWidget 类型，则将控件添加到内容布局的末尾，并显示搜索框容器控件。

        :param collapsibleWidget: 要添加的控件
        :return: 如果添加成功则返回 True，否则返回 False
        """
        if not self.isLocked():
            if isinstance(collapsibleWidget, CollapsibleFormWidget):
                # 显示搜索框容器控件
                self.searchBoxWidget.show()
                # 将控件添加到内容布局的末尾
                self.contentLayout.insertWidget(-1, collapsibleWidget)
                return True

    def showPropertyEditor(self):
        """
        显示属性编辑器对话框，将内容布局中的控件信息添加到属性编辑器中。
        """
        # 创建属性编辑器树控件
        tree = EditPropertiesTreeWidget()
        # 获取内容布局中的控件数量
        count = self.contentLayout.count()
        # 初始化文件夹字典
        folders = {}
        for i in range(count):
            # 获取布局中的项
            item = self.contentLayout.itemAt(i)
            # 获取项对应的控件
            w = item.widget()
            if w:
                # TODO: 修复未解析的特性引用问题
                if w.title() in ["Inputs"]:
                    # TODO: 修复未解析的特性引用问题
                    for key, group in w.groups.items():
                        if key not in folders:
                            folders[key] = {}
                        # for e in range(group.groupLayout.count()):
                        #    w = group.groupLayout.itemAt(e).widget()
                        #    folders[key][w.getLabel()] = group.groupLayout.itemAt(e).widget()

        for fold in folders:
            # 向属性编辑器树控件中添加文件夹
            tree.addFolder(fold)

        # 创建对话框
        d = QtWidgets.QDialog()
        # 设置对话框的布局为水平布局
        d.setLayout(QtWidgets.QHBoxLayout())
        # 将属性编辑器树控件添加到对话框布局中
        d.layout().addWidget(tree)
        # 显示对话框并进入模态状态
        d.exec_()


if __name__ == "__main__":
    import sys

    # 创建 Qt 应用程序实例
    app = QtWidgets.QApplication(sys.argv)

    # 创建滚动区域
    s = QtWidgets.QScrollArea()

    # 创建属性控件
    pw = PropertiesWidget()

    # 创建可折叠表单控件
    rootWidget = CollapsibleFormWidget(headName="Settings", noSpacer=True)
    # 向可折叠表单控件中添加属性条目
    rootWidget.addWidget("test", QtWidgets.QPushButton("ss"))
    rootWidget.addWidget("foo", QtWidgets.QPushButton(""))
    rootWidget.addWidget("bar", QtWidgets.QPushButton(""))

    # 创建另一个可折叠表单控件
    rootWidget2 = CollapsibleFormWidget(headName="Test", noSpacer=True)
    # 向可折叠表单控件中添加属性条目
    rootWidget2.addWidget("test2", QtWidgets.QPushButton("aa"))

    # 将可折叠表单控件添加到属性控件中
    pw.addWidget(rootWidget)
    pw.addWidget(rootWidget2)
    # 将属性控件设置为滚动区域的内容控件
    s.setWidget(pw)
    # 显示滚动区域
    s.show()

    # 清空属性控件的内容
    pw.clear()

    # 进入应用程序的事件循环
    sys.exit(app.exec_())